"""
路由系统单元测试

测试路由器、策略、弹性层、监控层等核心组件。

作者: RedFire Team
创建日期: 2025-10-06
"""

import pytest
import asyncio
from datetime import datetime
from unittest.mock import Mock, AsyncMock, MagicMock

from app.routing.router import DataSourceRouter
from app.routing.strategy import create_routing_context, RoutingPriority
from app.routing.strategies.priority import PriorityRoutingStrategy
from app.routing.strategies.capability_based import CapabilityBasedRoutingStrategy
from app.resilience.circuit_breaker import CircuitBreaker, CircuitBreakerState, CircuitBreakerOpenError
from app.resilience.rate_limiter import TokenBucketRateLimiter, RateLimitExceededError
from app.monitoring.health_monitor import HealthMonitor, HealthStatus
from app.adapters.core.capabilities import DataCapability, MarketCapability


# ==================== Fixtures ====================

@pytest.fixture
def mock_adapter():
    """创建模拟适配器"""
    adapter = Mock()
    adapter.provider_name = "mock_adapter"
    adapter.get_capabilities = Mock(return_value={
        'data_capabilities': [DataCapability.REALTIME_QUOTES],
        'market_capabilities': [MarketCapability.CN_A_SHARES]
    })
    return adapter


@pytest.fixture
def router():
    """创建路由器实例"""
    return DataSourceRouter(
        enable_auto_failover=True,
        max_failover_attempts=3
    )


@pytest.fixture
def priority_strategy():
    """创建优先级策略"""
    return PriorityRoutingStrategy(
        priority_list=["tushare", "akshare", "influxdb"],
        enable_fallback=True
    )


# ==================== 路由器测试 ====================

class TestDataSourceRouter:
    """路由器测试"""
    
    def test_router_initialization(self, router):
        """测试路由器初始化"""
        assert router.enable_auto_failover == True
        assert router.max_failover_attempts == 3
        assert router.registry is not None
    
    def test_register_adapter(self, router, mock_adapter):
        """测试适配器注册"""
        success = router.register_adapter(mock_adapter)
        assert success == True
        assert "mock_adapter" in router.get_all_adapters()
    
    def test_unregister_adapter(self, router, mock_adapter):
        """测试适配器注销"""
        router.register_adapter(mock_adapter)
        success = router.unregister_adapter("mock_adapter")
        assert success == True
        assert "mock_adapter" not in router.get_all_adapters()
    
    def test_add_remove_strategy(self, router, priority_strategy):
        """测试策略添加和移除"""
        router.add_strategy(priority_strategy)
        assert router.strategy.get_strategy_count() > 0
        
        router.remove_strategy("priority")
        # 组合策略仍然存在，只是内部策略被移除
        assert router.strategy.get_enabled_strategy_count() == 0
    
    @pytest.mark.asyncio
    async def test_route_success(self, router, mock_adapter):
        """测试路由成功"""
        router.register_adapter(mock_adapter)
        strategy = PriorityRoutingStrategy(priority_list=["mock_adapter"])
        router.add_strategy(strategy)
        
        context = create_routing_context("get_quote", symbols=["000001.SZ"])
        result = await router.route(context)
        
        assert result.is_valid()
        assert result.primary_adapter == "mock_adapter"
    
    @pytest.mark.asyncio
    async def test_route_no_adapters(self, router):
        """测试无适配器时路由"""
        context = create_routing_context("get_quote", symbols=["000001.SZ"])
        result = await router.route(context)
        
        assert not result.is_valid()
        assert result.confidence_score == 0.0


# ==================== 优先级策略测试 ====================

class TestPriorityRoutingStrategy:
    """优先级路由策略测试"""
    
    def test_strategy_initialization(self, priority_strategy):
        """测试策略初始化"""
        assert priority_strategy.name == "priority"
        assert priority_strategy.priority_list == ["tushare", "akshare", "influxdb"]
    
    @pytest.mark.asyncio
    async def test_select_by_priority(self, priority_strategy):
        """测试按优先级选择"""
        context = create_routing_context("get_quote", symbols=["000001.SZ"])
        available = ["influxdb", "akshare", "tushare"]
        
        result = await priority_strategy.select_adapter(context, available)
        
        # 应该选择优先级最高的tushare
        assert result.primary_adapter == "tushare"
        assert result.confidence_score == 1.0
    
    @pytest.mark.asyncio
    async def test_fallback_selection(self, priority_strategy):
        """测试备选适配器"""
        context = create_routing_context("get_quote", symbols=["000001.SZ"])
        available = ["influxdb", "akshare", "tushare"]
        
        result = await priority_strategy.select_adapter(context, available)
        
        # 应该有备选适配器
        assert len(result.fallback_adapters) > 0
        assert "akshare" in result.fallback_adapters
    
    def test_add_to_priority(self, priority_strategy):
        """测试添加到优先级列表"""
        priority_strategy.add_to_priority("new_adapter", position=0)
        assert priority_strategy.priority_list[0] == "new_adapter"
    
    def test_remove_from_priority(self, priority_strategy):
        """测试从优先级列表移除"""
        success = priority_strategy.remove_from_priority("akshare")
        assert success == True
        assert "akshare" not in priority_strategy.priority_list


# ==================== 熔断器测试 ====================

class TestCircuitBreaker:
    """熔断器测试"""
    
    @pytest.fixture
    def circuit_breaker(self):
        """创建熔断器"""
        from app.resilience.circuit_breaker import CircuitBreakerConfig
        config = CircuitBreakerConfig(
            failure_threshold=3,
            timeout=1.0
        )
        return CircuitBreaker("test_cb", config)
    
    def test_initial_state(self, circuit_breaker):
        """测试初始状态"""
        assert circuit_breaker.state == CircuitBreakerState.CLOSED
        assert circuit_breaker.is_closed
    
    @pytest.mark.asyncio
    async def test_successful_call(self, circuit_breaker):
        """测试成功调用"""
        async def success_func():
            return "success"
        
        result = await circuit_breaker.call(success_func)
        assert result == "success"
        assert circuit_breaker.is_closed
    
    @pytest.mark.asyncio
    async def test_failed_call(self, circuit_breaker):
        """测试失败调用"""
        async def fail_func():
            raise ValueError("error")
        
        with pytest.raises(ValueError):
            await circuit_breaker.call(fail_func)
    
    @pytest.mark.asyncio
    async def test_circuit_opens_after_failures(self, circuit_breaker):
        """测试连续失败后熔断器打开"""
        async def fail_func():
            raise ValueError("error")
        
        # 连续失败3次
        for _ in range(3):
            try:
                await circuit_breaker.call(fail_func)
            except ValueError:
                pass
        
        # 熔断器应该打开
        assert circuit_breaker.is_open
        
        # 再次调用应该直接被拒绝
        with pytest.raises(CircuitBreakerOpenError):
            await circuit_breaker.call(fail_func)
    
    @pytest.mark.asyncio
    async def test_half_open_state(self, circuit_breaker):
        """测试半开状态"""
        async def fail_func():
            raise ValueError("error")
        
        # 触发熔断
        for _ in range(3):
            try:
                await circuit_breaker.call(fail_func)
            except ValueError:
                pass
        
        assert circuit_breaker.is_open
        
        # 等待超时
        await asyncio.sleep(1.1)
        
        # 下一次调用应该进入半开状态
        try:
            await circuit_breaker.call(fail_func)
        except (ValueError, CircuitBreakerOpenError):
            pass
        
        # 状态可能是半开或打开（取决于调用结果）
        assert circuit_breaker.state in [CircuitBreakerState.HALF_OPEN, CircuitBreakerState.OPEN]


# ==================== 限流器测试 ====================

class TestTokenBucketRateLimiter:
    """令牌桶限流器测试"""
    
    @pytest.fixture
    def rate_limiter(self):
        """创建限流器"""
        return TokenBucketRateLimiter(
            name="test_limiter",
            rate=10.0,  # 每秒10个令牌
            capacity=20
        )
    
    @pytest.mark.asyncio
    async def test_acquire_success(self, rate_limiter):
        """测试获取令牌成功"""
        result = await rate_limiter.acquire()
        assert result == True
    
    @pytest.mark.asyncio
    async def test_acquire_failure_when_empty(self, rate_limiter):
        """测试令牌耗尽时获取失败"""
        # 耗尽所有令牌
        for _ in range(21):  # 容量是20
            await rate_limiter.acquire()
        
        # 再次获取应该失败
        result = await rate_limiter.acquire()
        assert result == False
    
    @pytest.mark.asyncio
    async def test_refill(self, rate_limiter):
        """测试令牌补充"""
        # 用掉一些令牌
        for _ in range(5):
            await rate_limiter.acquire()
        
        # 等待补充
        await asyncio.sleep(0.5)
        
        # 应该能再次获取
        result = await rate_limiter.acquire()
        assert result == True
    
    def test_get_stats(self, rate_limiter):
        """测试统计信息"""
        stats = rate_limiter.get_stats()
        assert 'rate' in stats
        assert 'capacity' in stats
        assert 'available_tokens' in stats


# ==================== 健康监控器测试 ====================

class TestHealthMonitor:
    """健康监控器测试"""
    
    @pytest.fixture
    def health_monitor(self):
        """创建健康监控器"""
        return HealthMonitor(
            check_interval=1.0,
            health_check_timeout=1.0,
            unhealthy_threshold=2
        )
    
    def test_monitor_initialization(self, health_monitor):
        """测试监控器初始化"""
        assert health_monitor.check_interval == 1.0
        assert health_monitor.unhealthy_threshold == 2
    
    def test_register_adapter(self, health_monitor, mock_adapter):
        """测试注册适配器"""
        health_monitor.register_adapter(mock_adapter)
        assert "mock_adapter" in health_monitor.adapters
    
    @pytest.mark.asyncio
    async def test_check_adapter_success(self, health_monitor, mock_adapter):
        """测试健康检查成功"""
        mock_adapter.health_check = AsyncMock()
        health_monitor.register_adapter(mock_adapter)
        
        result = await health_monitor.check_adapter("mock_adapter", mock_adapter)
        
        assert result.is_healthy()
        assert result.status == HealthStatus.HEALTHY
    
    @pytest.mark.asyncio
    async def test_check_adapter_timeout(self, health_monitor, mock_adapter):
        """测试健康检查超时"""
        async def slow_check():
            await asyncio.sleep(2)
        
        mock_adapter.health_check = slow_check
        health_monitor.register_adapter(mock_adapter)
        
        result = await health_monitor.check_adapter("mock_adapter", mock_adapter)
        
        assert not result.is_healthy()
        assert "超时" in result.error_message
    
    def test_get_health_report(self, health_monitor, mock_adapter):
        """测试获取健康报告"""
        health_monitor.register_adapter(mock_adapter)
        report = health_monitor.get_health_report()
        
        assert 'summary' in report
        assert 'adapters' in report
        assert report['summary']['total_adapters'] == 1


# ==================== 路由上下文测试 ====================

class TestRoutingContext:
    """路由上下文测试"""
    
    def test_create_context_basic(self):
        """测试创建基本上下文"""
        context = create_routing_context(
            "get_quote",
            symbols=["000001.SZ"]
        )
        
        assert context.request_type == "get_quote"
        assert "000001.SZ" in context.symbols
    
    def test_create_context_with_preferences(self):
        """测试创建带偏好的上下文"""
        context = create_routing_context(
            "get_quote",
            symbols=["000001.SZ"],
            preferred_providers=["tushare"],
            excluded_providers=["ctp"]
        )
        
        assert context.is_provider_preferred("tushare")
        assert context.is_provider_excluded("ctp")
    
    def test_create_context_with_capabilities(self):
        """测试创建带能力要求的上下文"""
        context = create_routing_context(
            "get_quote",
            symbols=["000001.SZ"],
            required_capabilities=["realtime_quotes"]
        )
        
        assert "realtime_quotes" in context.required_capabilities


# ==================== 集成测试 ====================

class TestRouterIntegration:
    """路由器集成测试"""
    
    @pytest.mark.asyncio
    async def test_full_routing_flow(self):
        """测试完整路由流程"""
        # 创建模拟适配器
        adapter1 = Mock()
        adapter1.provider_name = "adapter1"
        adapter1.get_capabilities = Mock(return_value={})
        adapter1.get_quote = AsyncMock(return_value={"price": 100})
        
        # 创建路由器
        router = DataSourceRouter()
        router.register_adapter(adapter1)
        
        # 添加策略
        strategy = PriorityRoutingStrategy(priority_list=["adapter1"])
        router.add_strategy(strategy)
        
        # 执行路由
        context = create_routing_context("get_quote", symbols=["000001.SZ"])
        
        # 使用路由+执行
        async def execute_func(adapter):
            return await adapter.get_quote()
        
        result = await router.route_with_execution(context, execute_func)
        
        assert result == {"price": 100}


if __name__ == "__main__":
    pytest.main([__file__, "-v", "-s"])

